週六清晨五點,南桃小鎮還沉浸在晨霧中,但市集現場已經燈火通明。小潔、大財、霈姊和阿美圍在「作戰指揮中心」——一個搭建在市集中央的白色帳篷裡,最後一次檢視所有系統。
「所有設備綠燈,」大財盯著筆電螢幕,「Google生態系運作完美,網路頻寬充足,備援系統待命。」
小潔深吸一口氣,看著Google Calendar上標示的倒數時間:「距離開幕還有三小時。我們準備好了嗎?」
霈姊抱著剛醒來的小肉丸,眼中既有緊張也有期待:「我從來沒想過我們真的能做到這樣的規模。」
阿美興奮地指著Google Analytics的即時數據:「妳們看!現在已經有超過五千人在線上關注我們,而且還在持續增加!」
上午七點,第一批參展商開始到場。讓小潔驚喜的是,除了原本報名的本地攤商,還有許多意想不到的參與者。
「葉候選人!」一位穿著傳統客家服飾的老婆婆向小潔招手,「我是製作客家美食的阿桂嬤,聽說妳們要辦市集,我特地趕來參加!」
阿桂嬤身後跟著一群年輕人,推著裝滿客家粄條、紅龜粿、艾草粿的推車。「這些是我們客家社區的年輕人,他們說要用AI幫我們推廣傳統美食!」
大財立刻啟動Google翻譯和資料收集系統:「太好了!我們馬上為您建立攤位資訊,並且用AI生成多語言介紹。」
不遠處,原住民歌手阿力正在調試音響設備。「我要唱傳統古謠,但用現代編曲,還要配合LED燈光秀!」他對著音控台興奮地說。
阿美拿起攝影機開始記錄:「這太棒了!我們本來只是想展示AI技術,結果變成了南桃文化的大匯演!」
上午八點,志工們陸續抵達。讓小潔意外的是,除了原本報名的大學生,還有許多意想不到的面孔。
「小潔姊!」一群穿著統一T恤的高中生跑了過來,「我們是南桃高中的學生會,聽說妳們在辦AI市集,我們全校都想來幫忙!」
學生會長小婷興奮地說:「我們有一百多個同學報名當志工,還有電腦社的同學想展示他們寫的程式!」
霈姊看著這群充滿活力的年輕人,眼中湧起感動:「這就是我們想要的效果。讓年輕人看到科技的美好,看到未來的可能性。」
Google Forms的志工管理系統立刻派上用場。大財快速地為新加入的志工分配任務:
「攤位引導組:負責協助參展商設置和解答問題」
「技術支援組:處理設備問題和系統維護」
「多語言服務組:協助外國朋友和新住民」
「活動記錄組:拍攝和直播活動過程」
「緊急應變組:處理突發狀況」
上午九點,小潔接到阿美慌張的電話:「小潔!妳快來看!有電視台來了!」
小潔急忙跑到市集入口,看到三台新聞車停在路邊,記者和攝影師正在架設設備。
「請問您是葉小潔候選人嗎?」一位年輕的女記者主動走過來,「我是民視新聞的記者林小芳。我們聽說南桃要舉辦全台第一個AI創意市集,特地來採訪!」
另一位年長的記者也走了過來:「我是公視的王記者。這個活動在網路上引起很大的討論,我們想了解這是如何辦到的。」
小潔既驚喜又緊張:「我們只是想用科技為鄉鎮帶來一些改變...沒想到會受到這麼多關注。」
大財立刻啟動媒體應對流程,Google Sheets裡的媒體聯絡清單立即派上用場:「我們已經準備好媒體資料包,包括活動介紹、技術說明和採訪行程安排。」
上午十點整,南桃AI創意市集正式開幕。小潔站在主舞台上,看著台下黑壓壓的人群,心中湧起難以言喻的激動。
「歡迎大家來到南桃AI創意市集!」小潔的聲音透過音響系統傳遍整個會場,「今天,我們要證明科技不是冷冰冰的工具,而是能夠溫暖人心、連結社群的橋樑!」
台下響起熱烈的掌聲。讓小潔驚喜的是,人群中不只有本地居民,還有許多外地遊客,甚至有外國面孔。
「現在,讓我們一起見證傳統與科技的美麗結合!」
隨著小潔的話音落下,整個市集瞬間活了起來。
阿桂嬤的攤位成為第一個亮點。大財協助她建立了一個AI美食推薦系統,透過Google Lens識別食材,即時生成營養分析和客家文化介紹。
「這個粄條有一百年的歷史,」阿桂嬤對著一群遊客說道,「我的阿婆傳給我的配方,現在用這個神奇的機器,可以告訴妳們每一種食材的營養價值和文化故事!」
遊客們紛紛拿出手機掃描QR code,立刻看到詳細的介紹:
【客家粄條 - AI美食介紹】
歷史:源自客家先民的智慧,使用在來米製作
營養:富含碳水化合物,低脂肪,容易消化
文化意義:代表客家人勤儉持家的精神
製作工藝:手工製作,需要精準的水量控制
推薦搭配:韭菜、豆芽菜、客家鹹菜
「太神奇了!」一位台北來的遊客驚嘆,「我從來不知道吃個粄條還有這麼多學問!」
阿力的表演更是驚艷全場。他的傳統古謠搭配AI生成的視覺效果,讓現場觀眾如癡如醉。
當阿力唱起祖先的歌謠時,LED螢幕上同步顯示著AI解讀的音樂情感,轉化成流動的色彩和圖案:
「這是我第一次『看見』音樂的靈魂,」一位音樂系的大學生激動地說,「科技讓我更深刻地理解原住民文化的美。」
南桃的農民們也積極參與。老農阿伯帶來了他種的有機蔬菜,在AI協助下建立了完整的產銷履歷系統。
遊客只要掃描蔬菜上的QR code,就能看到:
「我種菜四十年,從來沒想過可以這樣介紹我的菜,」阿伯笑得合不攏嘴,「年輕人真的很厲害!」
南桃高中的同學們也展示了他們的創意專案:
電腦社的同學們開發了一個AI詩歌創作系統,遊客可以輸入關於南桃的關鍵字,AI就會即時創作一首詩:
輸入:「南桃、夕陽、思念」
AI創作:
「南桃小鎮夕陽西,
思念如潮湧心扉,
科技雖新情不變,
故鄉永遠在心間。」
「哇!這比我寫得還好!」一位小朋友驚嘆道。
美術社和電腦社合作,用VR技術重現了南桃五十年前的樣貌,讓遊客可以體驗小鎮的歷史變遷。
戴上VR眼鏡的阿嬤感動得流下眼淚:「這就是我小時候的南桃!那棟老房子、那條小河,都還在!」
中午時分,一群來自台北的年輕創業者出現在市集裡。
「我們在網路上看到這個活動,覺得太有意思了!」領隊的創業者小宇說,「我們想在這裡展示我們開發的農業無人機技術。」
他們現場展示了如何用無人機監測農田狀況,並用AI分析農作物的健康程度。現場的農民們圍觀得津津有味。
「這個可以幫我們省很多時間!」阿伯興奮地說,「以前要走遍整片田才知道哪裡有問題,現在飛一圈就知道了!」
更令人意外的是,幾位歐美觀光客也被吸引過來。
「This is amazing!」一位來自德國的遊客Tom驚嘆,「我們在台北聽說這裡有個特別的AI市集,就租車過來看看。這比任何科技展都有趣!」
他的朋友Sarah補充:「在歐洲,我們很少看到科技和傳統文化結合得這麼好的例子。這真是台灣的驕傲!」
大財立刻啟動多語言服務,為外國朋友提供英語導覽,介紹每個攤位的特色和背後的技術原理。
下午兩點,阿美發現了一個驚人的現象:「小潔!妳快看網路上的反應!」
Google Analytics顯示,活動官網的流量在短短幾小時內暴增了五十倍。社群媒體上,#南桃AI市集 的hashtag正在瘋狂傳播。
Facebook上的熱門貼文:
「誰說鄉下地方沒有創新?南桃這個AI市集太驚豔了!傳統客家美食配上AI介紹,我第一次覺得科技這麼有溫度!」(分享數:3,847)
Instagram上的美照:
原住民歌手配上AI視覺效果的照片,獲得了超過一萬個讚,數百則留言都在問「這是在哪裡?」
YouTube直播:
現場直播的觀看人數突破十萬,留言區湧入來自全台各地甚至海外的支持訊息。
「這太不可思議了,」霈姊看著手機螢幕上不斷跳動的數字,「我們只是想辦個小市集,怎麼變成全國關注的焦點了?」
最讓小潔感動的,是看到原本對她持保留態度的居民們,開始主動參與和支持。
一直對小潔的「外來者」身份有意見的李大嬸,主動走到小潔面前:
「小潔啊,我要跟妳道歉,」李大嬸的眼中含著淚水,「我以前覺得妳是台北來的,不了解我們鄉下,但今天看到這個市集,我知道我錯了。」
她指著正在排隊購買客家美食的遊客群:「妳讓全台灣的人都知道我們南桃有多好,讓我們的文化被看見。這是我們自己都做不到的事情。」
更令人振奮的是,一些原本在外地工作的南桃年輕人,特地請假回來參加活動。
「我在台中工作三年了,從來沒這麼以南桃為榮過,」返鄉的年輕人小明說,「看到家鄉可以這麼有活力,我在考慮是不是該回來發展。」
他的朋友小華也點頭同意:「對啊,如果南桃真的能變成這樣有創意的地方,我們為什麼還要在外面漂泊?」
最保守的鎮民代表陳老爹,也在下午時主動找到小潔:
「小潔,我要承認,我小看妳了,」陳老爹誠懇地說,「我活了七十年,從來沒看過南桃這麼熱鬧,這麼有生氣。妳做到了我們這些老頭子想都不敢想的事情。」
他看著滿滿的人潮和熱絡的活動:「如果妳真的當選,我會全力支持妳的政策。」
下午三點,各大電視台開始播出南桃AI市集的新聞報導:
民視新聞:
「南桃小鎮今天舉辦了全台第一個AI創意市集,結合傳統文化與現代科技,吸引了數千人參與,展現了台灣鄉鎮創新的無限可能...」
公視新聞:
「這個由候選人葉小潔發起的活動,不只是一場市集,更是一場科技與人文的對話,讓我們看到AI技術如何真正服務社區,提升在地文化的價值...」
TVBS新聞:
「南桃AI市集的成功,為台灣的鄉鎮發展提供了新的思路,科技不再是都市的專利,也能在鄉村綻放出不同的光彩...」
大財興奮地監控著各種媒體的報導:「我們上了五家電視台的新聞,十幾個網路媒體都在報導,這個影響力太驚人了!」
傍晚時分,小潔接到一通意想不到的電話。
「請問是葉小潔小姐嗎?我是台大資工系的張教授,」電話那頭傳來溫和的聲音,「我們在網路上看到南桃AI市集的報導,非常感興趣。我們想邀請妳到台大分享這個經驗。」
緊接著,成大、清大、政大的教授們也陸續來電,希望深入了解他們的AI應用模式。
「妳們的實作案例太有價值了,」台科大的李教授在電話中說,「這是AI技術真正落地應用的典範,我們希望能將這個模式推廣到更多鄉鎮。」
阿美難以置信地說:「我們從學生變成了老師的研究對象?這個世界變化太快了!」
劉校長和他的團隊在辦公室裡緊急開會,看著電視上南桃AI市集的報導,每個人的臉色都很凝重。
「這個影響力太大了,」劉校長的競選總幹事焦急地說,「所有媒體都在正面報導,我們要如何回應?」
劉校長盯著螢幕上小潔被民眾簇擁的畫面,眼中閃爍著複雜的情緒。
晚上八點,市集正式結束。大財統計出令人驚嘆的數據:
參與人數:
經濟效益:
技術表現:
「這些數字太不真實了,」霈姊看著報表,「我們真的做到了這樣的成果?」
晚上十點,市集現場已經恢復平靜。小潔、大財、霈姊和阿美坐在主舞台上,看著滿天星斗,回味這一天的奇蹟。
「我們成功了,」小潔輕聲說道,「但這只是開始。」
大財點點頭:「今天證明了AI技術可以真正服務社區,讓傳統文化重新發光。這為我們的未來政策提供了強大的信心。」
霈姊抱著已經睡著的小肉丸:「我希望小肉丸長大後,能生活在一個科技與人文和諧共存的世界。今天,我們朝這個目標邁出了重要的一步。」
阿美舉起手機,拍下這個歷史性的時刻:「這一天會被記錄在南桃的歷史上,也會被記錄在台灣AI發展的歷史上。」
隔天的報紙頭版,幾乎都以南桃AI市集為主題:
聯合報頭版:
「南桃奇蹟:AI如何改變台灣鄉鎮」
自由時報頭版:
「科技下鄉新典範:南桃模式啟示錄」
蘋果日報頭版:
「25歲候選人掀起AI革命」
更令人驚喜的是,國際媒體也開始關注南桃的AI市集:
BBC亞洲版:
「Taiwan's Rural Town Shows How AI Can Preserve Culture」
(台灣鄉鎮展示AI如何保存文化)
CNN國際版:
「Small Town, Big Ideas: Taiwan's AI Revolution」
(小鎮大思維:台灣的AI革命)
日本NHK:
「台湾の地方都市が示すAI活用の新しい可能性」
(台灣地方城市展示AI應用的新可能性)
市集結束後,南桃的觀光人潮並沒有減少。每個週末都有數百位遊客專程前來,希望體驗「小鎮」的魅力。
當地的民宿、餐廳、商店生意都比以前好了三倍以上。
「我們要感謝小潔,」民宿老闆娘開心地說,「她讓世界看見了南桃。」
更重要的是,外出工作的年輕人開始有返鄉的念頭。
「家鄉變得這麼有前景,我為什麼還要在外面打拼?」許多年輕人開始這樣思考。
南桃的人口外流問題,幾十年來第一次出現逆轉的跡象。
傳統農業也開始導入AI技術,提升產品品質和行銷能力。
小型製造業也在評估導入自動化和AI管理系統的可能性。
南桃正在經歷一場安靜但深刻的產業革命。
成功也意味著更高的期望。民眾希望看到更多創新專案,媒體持續關注後續發展,政府部門也期待更多政策建議。
「壓力很大,」小潔私下對霈姊說,「但這也是我們證明自己的機會。」
夜深了,南桃小鎮重歸寧靜。但這份寧靜與之前不同,它充滿了希望和可能性。
小潔站在咖啡店的陽台上,看著遠處依然閃爍的市集燈光,心中五味雜陳。
一天前,她還只是一個充滿理想但名不見經傳的年輕候選人。現在,她成為了全國關注的焦點,肩負著無數人的期望。
「這真的是我們想要的嗎?」她問自己。
但當她看到大財還在筆電前工作,阿美還在整理今天的照片和影片,霈姊還在哄著小肉丸睡覺時,她知道答案是肯定的。
他們要的不是名聲,不是關注,而是真正改變這個世界的可能性。
而今天,他們證明了這個可能性是真實存在的。
明天,新的挑戰就會開始。但今晚,他們可以為這個小小的奇蹟感到驕傲。
南桃AI創意市集的成功,不只是一場活動的勝利,更是一個新時代的開始。
在這個新時代裡,科技不再是冷冰冰的工具,而是溫暖人心的橋樑;鄉村不再是被遺忘的角落,而是創新的前沿;年輕人不再是政治的局外人,而是改變的主力軍。
這一切,都始於四個年輕人的夢想,和一個小鎮對未來的信心。
下節預告:成功的喜悅還未散去,新的挑戰就要來臨。小潔團隊將面臨AI技術應用中最複雜的考驗——如何在多元文化的衝突中找到平衡...
若迫不及待想要知道之後的故事發展,可以到鏡文學,故事的部分,我已經都上傳到這裡,歡迎使用打賞功能等📚,是對筆者最實質的鼓勵🥰。ps:實做的部分還是會只放在鐵人賽喔
我是 Wolke。我是一名專業程式開發者,專長是開發 AI 和程式解決方案。
我投入了不少時間在專業發展上。我是多本書的作者,其中包括《LINE聊天機器人+AI+雲端+開源+程式:輕鬆入門到完整學習》和《ChatGPT來襲,未來人人都需具備的運算思維!應用詠唱工程來釋放程式生產力—程式學習/開發篇》。也有出版線上課程,我熱衷於分享我的經驗和技術,幫助其他開發者更好地利用 AI 工具。
也在許多知名大學、論壇、社團擔任講者,如果貴方有需要也歡迎與我聯繫。
2023年 講座 紀錄
最後這篇文章若有切合你的需求,敬請訂閱按讚分享
本系列相關內容已轉載及加強到筆者 2025 年 所出版之
若這篇文章對您有實質幫助🙏,還望購買書籍📚,是對筆者最實質的鼓勵🥰。
本實作基於故事-08-01中南桃AI創意市集的成功案例,展示如何整合多種AI技術和Google生態系,打造一個完整的智慧市集管理與體驗系統。
graph TB
A[Google Workspace核心] --> B[Google Sheets數據中樞]
A --> C[Google Calendar時程管理]
A --> D[Google Sites官網]
A --> E[Google Forms報名系統]
A --> F[Google Drive檔案管理]
B --> G[參展商管理]
B --> H[志工排班]
B --> I[設備監控]
B --> J[預算控制]
B --> K[效果統計]
L[AI應用層] --> M[美食推薦系統]
L --> N[音樂視覺化]
L --> O[智慧行銷]
L --> P[詩歌創作]
L --> Q[多語言服務]
R[前端體驗] --> S[QR Code掃描]
R --> T[VR歷史體驗]
R --> U[即時直播]
R --> V[社群分享]
Google Sheets作為核心數據庫
// 參展商資料庫結構
const exhibitorData = {
id: "EXH001",
name: "阿桂嬤客家美食",
category: "傳統美食",
location: "A區01號",
contact: "0912-345-678",
products: ["客家粄條", "紅龜粿", "艾草粿"],
aiFeatures: ["營養分析", "文化介紹", "多語言說明"],
status: "已入場",
timestamp: "2024-10-26 07:30:00"
};
// 志工排班系統
const volunteerSchedule = {
id: "VOL001",
name: "南桃高中小婷",
group: "攤位引導組",
shift: "09:00-12:00",
location: "A區",
skills: ["中文", "英文", "基礎客語"],
status: "已報到",
emergencyContact: "0987-654-321"
};
// 即時監控數據
const realTimeMonitoring = {
timestamp: new Date(),
visitors: 8000,
onlineViewers: 120000,
systemStatus: "正常",
networkUsage: "75%",
powerConsumption: "正常",
emergencyReports: 0
};
// 自動化數據同步腳本
function autoSyncData() {
const sheet = SpreadsheetApp.getActiveSheet();
const calendar = CalendarApp.getDefaultCalendar();
const drive = DriveApp;
// 同步參展商資料到日曆
syncExhibitorToCalendar();
// 自動生成QR Code
generateQRCodes();
// 發送即時通知
sendRealTimeNotifications();
// 更新網站內容
updateWebsiteContent();
}
function syncExhibitorToCalendar() {
const exhibitors = getExhibitorData();
exhibitors.forEach(exhibitor => {
const event = calendar.createEvent(
`${exhibitor.name} - ${exhibitor.category}`,
new Date('2024-10-26 10:00:00'),
new Date('2024-10-26 18:00:00'),
{
description: `攤位:${exhibitor.location}\n聯絡:${exhibitor.contact}`,
location: exhibitor.location
}
);
});
}
<!-- 嵌入式攤位地圖 -->
<div id="exhibitor-map">
<script>
// 從Google Sheets即時載入攤位資訊
function loadExhibitorMap() {
const sheetUrl = 'https://docs.google.com/spreadsheets/d/YOUR_SHEET_ID';
fetch(`${sheetUrl}/gviz/tq?tqx=out:json`)
.then(response => response.text())
.then(data => {
const jsonData = JSON.parse(data.substr(47).slice(0, -2));
renderMap(jsonData);
});
}
function renderMap(data) {
const mapContainer = document.getElementById('map-container');
data.table.rows.forEach(row => {
const exhibitor = {
name: row.c[0].v,
location: row.c[1].v,
category: row.c[2].v,
features: row.c[3].v
};
const marker = createMapMarker(exhibitor);
mapContainer.appendChild(marker);
});
}
</script>
</div>
import google.generativeai as genai
from google.cloud import vision
import qrcode
import json
class HakkaFoodAI:
def __init__(self, api_key):
genai.configure(api_key=api_key)
self.model = genai.GenerativeModel('gemini-pro')
self.vision_client = vision.ImageAnnotatorClient()
def analyze_food(self, image_path):
"""分析食物圖片並生成介紹"""
# 使用Google Vision API識別食物
with open(image_path, 'rb') as image_file:
content = image_file.read()
image = vision.Image(content=content)
response = self.vision_client.label_detection(image=image)
labels = [label.description for label in response.label_annotations]
# 使用Gemini生成詳細介紹
prompt = f"""
根據以下食物標籤,生成客家美食的詳細介紹:
食物標籤:{', '.join(labels)}
請包含:
1. 歷史背景
2. 營養價值
3. 文化意義
4. 製作工藝
5. 推薦搭配
以JSON格式回應,包含中文、英文、日文三種語言版本。
"""
response = self.model.generate_content(prompt)
return json.loads(response.text)
def generate_qr_code(self, food_info, food_id):
"""生成包含食物資訊的QR Code"""
# 建立食物資訊URL
info_url = f"https://nantao-ai-market.com/food/{food_id}"
# 生成QR Code
qr = qrcode.QRCode(version=1, box_size=10, border=5)
qr.add_data(info_url)
qr.make(fit=True)
img = qr.make_image(fill_color="black", back_color="white")
img.save(f"qr_codes/food_{food_id}.png")
return info_url
# 使用範例
food_ai = HakkaFoodAI(api_key="YOUR_GEMINI_API_KEY")
# 分析阿桂嬤的客家粄條
food_info = food_ai.analyze_food("images/hakka_noodles.jpg")
qr_url = food_ai.generate_qr_code(food_info, "hakka_noodles")
print("客家粄條AI分析結果:")
print(json.dumps(food_info, ensure_ascii=False, indent=2))
import librosa
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation
import cv2
from openai import OpenAI
class MusicVisualizationAI:
def __init__(self, openai_key):
self.client = OpenAI(api_key=openai_key)
def analyze_audio_emotions(self, audio_file):
"""分析音樂情感並生成視覺元素"""
# 載入音訊檔案
y, sr = librosa.load(audio_file)
# 提取音樂特徵
features = {
'tempo': librosa.feature.tempo(y=y, sr=sr)[0],
'spectral_centroid': np.mean(librosa.feature.spectral_centroid(y=y, sr=sr)),
'mfcc': np.mean(librosa.feature.mfcc(y=y, sr=sr), axis=1),
'chroma': np.mean(librosa.feature.chroma(y=y, sr=sr), axis=1),
'zero_crossing_rate': np.mean(librosa.feature.zero_crossing_rate(y))
}
# 使用AI分析情感
emotion_analysis = self.analyze_emotions_with_ai(features)
# 生成視覺映射
visual_mapping = self.create_visual_mapping(emotion_analysis, features)
return visual_mapping
def analyze_emotions_with_ai(self, features):
"""使用AI分析音樂情感"""
prompt = f"""
根據以下音樂特徵分析情感:
- 節拍:{features['tempo']} BPM
- 光譜質心:{features['spectral_centroid']}
- 過零率:{features['zero_crossing_rate']}
請分析這段音樂的:
1. 主要情感(快樂、悲傷、平靜、激昂等)
2. 能量等級(1-10)
3. 建議的視覺色彩(主色調、輔助色)
4. 視覺動態(緩慢、中等、快速)
5. 圖案建議(山巒、流水、火焰、雲朵等)
以JSON格式回應。
"""
response = self.client.chat.completions.create(
model="gpt-4",
messages=[{"role": "user", "content": prompt}]
)
return json.loads(response.choices[0].message.content)
def create_visual_mapping(self, emotions, features):
"""建立視覺映射規則"""
mapping = {
'colors': {
'primary': emotions.get('primary_color', '#0066CC'),
'secondary': emotions.get('secondary_color', '#FFD700'),
'accent': emotions.get('accent_color', '#FF6B6B')
},
'patterns': {
'shape': emotions.get('pattern_type', 'wave'),
'movement': emotions.get('movement_style', 'flowing'),
'intensity': emotions.get('energy_level', 5)
},
'dynamics': {
'tempo_mapping': features['tempo'],
'amplitude_mapping': 'spectral_centroid',
'frequency_mapping': 'mfcc'
}
}
return mapping
def generate_realtime_visuals(self, audio_stream, led_controller):
"""即時生成視覺效果"""
while True:
# 即時分析音訊
chunk = audio_stream.read(1024)
features = self.extract_realtime_features(chunk)
# 生成視覺效果
visual_data = self.features_to_visual(features)
# 發送到LED控制器
led_controller.update_display(visual_data)
# 使用範例
music_ai = MusicVisualizationAI(openai_key="YOUR_OPENAI_KEY")
# 分析阿力的原住民古謠
visual_mapping = music_ai.analyze_audio_emotions("audio/indigenous_song.wav")
print("音樂視覺化分析:")
print(json.dumps(visual_mapping, ensure_ascii=False, indent=2))
from google.cloud import firestore
import google.generativeai as genai
from datetime import datetime
import uuid
class SmartFarmMarketingAI:
def __init__(self, gemini_key):
genai.configure(api_key=gemini_key)
self.model = genai.GenerativeModel('gemini-pro')
self.db = firestore.Client()
def create_product_profile(self, farmer_info, product_info, images):
"""為農產品建立完整的數位檔案"""
profile_id = str(uuid.uuid4())
# 使用AI生成產品描述
product_description = self.generate_product_story(farmer_info, product_info)
# 分析營養成分
nutrition_analysis = self.analyze_nutrition(product_info)
# 生成行銷文案
marketing_content = self.generate_marketing_content(product_description, nutrition_analysis)
# 建立追蹤系統
tracking_system = self.setup_tracking_system(profile_id, farmer_info)
# 儲存到Firestore
product_profile = {
'id': profile_id,
'farmer': farmer_info,
'product': product_info,
'description': product_description,
'nutrition': nutrition_analysis,
'marketing': marketing_content,
'tracking': tracking_system,
'images': images,
'created_at': datetime.now(),
'qr_code': self.generate_qr_code(profile_id)
}
self.db.collection('products').document(profile_id).set(product_profile)
return product_profile
def generate_product_story(self, farmer_info, product_info):
"""使用AI生成產品故事"""
prompt = f"""
為以下農產品生成吸引人的故事:
農民資訊:
- 姓名:{farmer_info['name']}
- 經驗:{farmer_info['experience']}
- 種植方式:{farmer_info['farming_method']}
- 農場位置:{farmer_info['location']}
產品資訊:
- 名稱:{product_info['name']}
- 品種:{product_info['variety']}
- 種植週期:{product_info['growing_period']}
- 特色:{product_info['features']}
請生成:
1. 農民故事(200字)
2. 產品特色介紹(150字)
3. 種植過程說明(200字)
4. 品質保證說明(100字)
5. 烹飪建議(100字)
以溫馨、真實的語調撰寫,強調有機、安全、用心的特點。
"""
response = self.model.generate_content(prompt)
return response.text
def analyze_nutrition(self, product_info):
"""分析營養成分並生成說明"""
prompt = f"""
為{product_info['name']}提供詳細的營養分析:
產品資訊:
- 名稱:{product_info['name']}
- 品種:{product_info['variety']}
- 有機認證:{product_info.get('organic', 'N/A')}
請提供:
1. 主要營養成分含量
2. 維生素和礦物質
3. 健康益處
4. 適合人群
5. 食用建議
6. 保存方法
以專業但易懂的方式說明。
"""
response = self.model.generate_content(prompt)
return response.text
def setup_tracking_system(self, product_id, farmer_info):
"""建立產品追蹤系統"""
tracking_data = {
'planting_date': farmer_info.get('planting_date'),
'harvest_date': farmer_info.get('harvest_date'),
'growing_photos': [],
'weather_data': [],
'care_records': [],
'quality_tests': [],
'certifications': farmer_info.get('certifications', [])
}
return tracking_data
# 使用範例
farm_ai = SmartFarmMarketingAI(gemini_key="YOUR_GEMINI_API_KEY")
# 為阿伯的有機蔬菜建立檔案
farmer_info = {
'name': '阿伯',
'experience': '40年',
'farming_method': '有機無毒',
'location': '南桃鎮',
'planting_date': '2024-08-01',
'harvest_date': '2024-10-15',
'certifications': ['有機認證', '產銷履歷']
}
product_info = {
'name': '有機高麗菜',
'variety': '台農一號',
'growing_period': '75天',
'features': ['無農藥', '甜度高', '口感脆嫩'],
'organic': True
}
images = ['cabbage_growing.jpg', 'cabbage_harvest.jpg', 'cabbage_final.jpg']
product_profile = farm_ai.create_product_profile(farmer_info, product_info, images)
print("農產品檔案建立完成:")
print(f"產品ID:{product_profile['id']}")
print(f"QR Code:{product_profile['qr_code']}")
import google.generativeai as genai
from textblob import TextBlob
import random
class AIPoetrySystem:
def __init__(self, api_key):
genai.configure(api_key=api_key)
self.model = genai.GenerativeModel('gemini-pro')
# 南桃主題詞庫
self.nantao_themes = {
'nature': ['山', '水', '田', '花', '樹', '風', '雲', '陽光', '月光', '星空'],
'culture': ['客家', '原住民', '傳統', '美食', '歌謠', '節慶', '工藝', '故事'],
'emotions': ['思念', '溫暖', '希望', '夢想', '愛', '友情', '回憶', '感動'],
'technology': ['AI', '科技', '創新', '未來', '連結', '智慧', '數位', '進步'],
'location': ['南桃', '小鎮', '故鄉', '家園', '社區', '鄰居', '街道', '市集']
}
def create_poem(self, keywords, style='modern', length='short'):
"""根據關鍵字創作詩歌"""
# 分析關鍵字情感
emotion_analysis = self.analyze_keywords_emotion(keywords)
# 擴展相關詞彙
expanded_themes = self.expand_themes(keywords)
# 生成詩歌
poem = self.generate_poem_with_ai(keywords, expanded_themes, emotion_analysis, style, length)
# 後處理和美化
formatted_poem = self.format_poem(poem)
return {
'poem': formatted_poem,
'keywords': keywords,
'emotion': emotion_analysis,
'style': style,
'length': length,
'created_at': datetime.now().isoformat()
}
def analyze_keywords_emotion(self, keywords):
"""分析關鍵字的情感傾向"""
combined_text = ' '.join(keywords)
blob = TextBlob(combined_text)
# 簡單的情感分析
polarity = blob.sentiment.polarity
if polarity > 0.1:
emotion = '正面愉悅'
elif polarity < -0.1:
emotion = '憂鬱深沉'
else:
emotion = '平和寧靜'
return emotion
def expand_themes(self, keywords):
"""根據關鍵字擴展主題詞彙"""
expanded = []
for keyword in keywords:
for category, words in self.nantao_themes.items():
if keyword in words or any(word in keyword for word in words):
expanded.extend(random.sample(words, min(3, len(words))))
# 去重並限制數量
expanded = list(set(expanded))[:10]
return expanded
def generate_poem_with_ai(self, keywords, themes, emotion, style, length):
"""使用AI生成詩歌"""
# 根據長度設定行數
line_count = {
'short': '4行',
'medium': '8行',
'long': '12行'
}.get(length, '4行')
prompt = f"""
請為南桃小鎮創作一首{style}風格的詩歌:
關鍵字:{', '.join(keywords)}
主題詞彙:{', '.join(themes)}
情感基調:{emotion}
詩歌長度:{line_count}
創作要求:
1. 體現南桃小鎮的特色和魅力
2. 融入科技與傳統的和諧
3. 表達對家鄉的情感
4. 使用富有詩意的語言
5. 每行字數控制在5-7字
6. 具有韻律感
請直接輸出詩歌內容,不需要其他說明。
"""
response = self.model.generate_content(prompt)
return response.text.strip()
def format_poem(self, poem_text):
"""格式化詩歌輸出"""
lines = [line.strip() for line in poem_text.split('\n') if line.strip()]
# 確保每行長度適中
formatted_lines = []
for line in lines:
if len(line) > 10: # 如果行太長,嘗試分割
# 簡單的分割邏輯
mid = len(line) // 2
formatted_lines.append(line[:mid])
formatted_lines.append(line[mid:])
else:
formatted_lines.append(line)
return '\n'.join(formatted_lines)
def create_interactive_interface(self):
"""建立互動介面"""
interface_html = """
<!DOCTYPE html>
<html>
<head>
<title>南桃AI詩人</title>
<meta charset="UTF-8">
<style>
body { font-family: '微軟正黑體', sans-serif; }
.container { max-width: 600px; margin: 0 auto; padding: 20px; }
.input-group { margin: 20px 0; }
.poem-output {
background: #f9f9f9;
padding: 20px;
border-radius: 10px;
margin: 20px 0;
line-height: 1.8;
font-size: 18px;
}
button {
background: #4CAF50;
color: white;
padding: 10px 20px;
border: none;
border-radius: 5px;
cursor: pointer;
}
</style>
</head>
<body>
<div class="container">
<h1>🎭 南桃AI詩人</h1>
<p>輸入關於南桃的關鍵字,AI將為您創作專屬詩歌</p>
<div class="input-group">
<label>關鍵字(用逗號分隔):</label><br>
<input type="text" id="keywords" placeholder="例如:南桃,夕陽,思念" style="width: 100%; padding: 10px;">
</div>
<div class="input-group">
<label>詩歌風格:</label><br>
<select id="style" style="padding: 10px;">
<option value="modern">現代詩</option>
<option value="classical">古典詩</option>
<option value="folk">民謠風</option>
</select>
</div>
<div class="input-group">
<label>詩歌長度:</label><br>
<select id="length" style="padding: 10px;">
<option value="short">短詩(4行)</option>
<option value="medium">中詩(8行)</option>
<option value="long">長詩(12行)</option>
</select>
</div>
<button onclick="createPoem()">🎨 創作詩歌</button>
<div id="poem-result" class="poem-output" style="display: none;">
<h3>您的專屬詩歌:</h3>
<div id="poem-content"></div>
<br>
<small>創作時間:<span id="creation-time"></span></small>
</div>
</div>
<script>
async function createPoem() {
const keywords = document.getElementById('keywords').value.split(',');
const style = document.getElementById('style').value;
const length = document.getElementById('length').value;
if (!keywords[0].trim()) {
alert('請輸入關鍵字');
return;
}
// 調用API創作詩歌
const response = await fetch('/api/create-poem', {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({keywords, style, length})
});
const result = await response.json();
// 顯示結果
document.getElementById('poem-content').innerHTML =
result.poem.replace(/\n/g, '<br>');
document.getElementById('creation-time').textContent =
new Date(result.created_at).toLocaleString();
document.getElementById('poem-result').style.display = 'block';
}
</script>
</body>
</html>
"""
return interface_html
# 使用範例
poetry_ai = AIPoetrySystem(api_key="YOUR_GEMINI_API_KEY")
# 創作示例詩歌
keywords = ['南桃', '夕陽', '思念']
poem_result = poetry_ai.create_poem(keywords, style='modern', length='short')
print("AI創作的詩歌:")
print(poem_result['poem'])
print(f"\n情感分析:{poem_result['emotion']}")
using UnityEngine;
using UnityEngine.XR;
using System.Collections;
public class NantaoHistoryVR : MonoBehaviour
{
[Header("場景設定")]
public GameObject[] historicalScenes;
public AudioClip[] narratorAudio;
[Header("互動元素")]
public GameObject[] interactiveObjects;
public Transform[] teleportPoints;
private int currentSceneIndex = 0;
private VRController vrController;
private AudioSource audioSource;
void Start()
{
vrController = FindObjectOfType<VRController>();
audioSource = GetComponent<AudioSource>();
// 初始化第一個歷史場景
LoadHistoricalScene(0);
}
public void LoadHistoricalScene(int sceneIndex)
{
// 隱藏所有場景
foreach (GameObject scene in historicalScenes)
{
scene.SetActive(false);
}
// 顯示指定場景
if (sceneIndex < historicalScenes.Length)
{
historicalScenes[sceneIndex].SetActive(true);
currentSceneIndex = sceneIndex;
// 播放對應的旁白
PlayNarration(sceneIndex);
// 設定互動元素
SetupInteractiveElements(sceneIndex);
}
}
void PlayNarration(int sceneIndex)
{
if (sceneIndex < narratorAudio.Length)
{
audioSource.clip = narratorAudio[sceneIndex];
audioSource.Play();
// 顯示字幕
ShowSubtitles(sceneIndex);
}
}
void ShowSubtitles(int sceneIndex)
{
string[] subtitles = {
"這是50年前的南桃小鎮,當時還是純樸的農村...",
"客家先民在這裡開墾,建立了美麗的家園...",
"原住民朋友與客家鄉親和諧共處,形成多元文化..."
};
if (sceneIndex < subtitles.Length)
{
UIManager.Instance.ShowSubtitle(subtitles[sceneIndex]);
}
}
void SetupInteractiveElements(int sceneIndex)
{
// 根據場景設定不同的互動元素
foreach (GameObject obj in interactiveObjects)
{
VRInteractable interactable = obj.GetComponent<VRInteractable>();
if (interactable != null)
{
interactable.SetSceneContext(sceneIndex);
}
}
}
// VR控制器輸入處理
void Update()
{
// 檢測控制器輸入
if (vrController.GetButtonDown(VRButton.Trigger))
{
HandleTriggerPress();
}
if (vrController.GetButtonDown(VRButton.Menu))
{
ShowTimelineMenu();
}
}
void HandleTriggerPress()
{
// 射線檢測互動物件
RaycastHit hit;
if (Physics.Raycast(vrController.transform.position,
vrController.transform.forward, out hit))
{
VRInteractable interactable = hit.collider.GetComponent<VRInteractable>();
if (interactable != null)
{
interactable.OnInteract();
}
}
}
void ShowTimelineMenu()
{
// 顯示時間軸選單,讓使用者選擇不同時期
TimelineUI.Instance.Show(currentSceneIndex);
}
}
public class VRInteractable : MonoBehaviour
{
[Header("互動設定")]
public string interactionText;
public AudioClip interactionAudio;
public GameObject[] activateObjects;
private int sceneContext;
public void SetSceneContext(int context)
{
sceneContext = context;
}
public void OnInteract()
{
// 播放互動音效
if (interactionAudio != null)
{
AudioSource.PlayClipAtPoint(interactionAudio, transform.position);
}
// 顯示互動文字
UIManager.Instance.ShowInteractionText(interactionText);
// 啟動相關物件
foreach (GameObject obj in activateObjects)
{
obj.SetActive(true);
}
// 觸發特殊事件
TriggerSpecialEvent();
}
void TriggerSpecialEvent()
{
switch (gameObject.name)
{
case "OldHouse":
ShowHouseHistory();
break;
case "RiverBridge":
ShowBridgeEvolution();
break;
case "TraditionalFarm":
ShowFarmingEvolution();
break;
}
}
void ShowHouseHistory()
{
string[] houseStory = {
"這棟老房子建於1970年,是典型的客家三合院...",
"屋主是當地的農民,世代在這裡生活...",
"房子見證了南桃小鎮的變遷..."
};
StartCoroutine(PlayStorySequence(houseStory));
}
IEnumerator PlayStorySequence(string[] story)
{
foreach (string line in story)
{
UIManager.Instance.ShowInteractionText(line);
yield return new WaitForSeconds(3f);
}
}
}
class HistoricalDataManager:
def __init__(self):
self.historical_periods = {
'1970s': {
'description': '純樸農村時期',
'buildings': ['三合院', '穀倉', '水井', '田埂'],
'activities': ['插秧', '收割', '曬穀', '養雞'],
'population': 1200,
'main_industry': '農業'
},
'1990s': {
'description': '工業化初期',
'buildings': ['三合院', '工廠', '新式住宅', '柏油路'],
'activities': ['農業', '小型製造業', '通勤工作'],
'population': 2500,
'main_industry': '農業+製造業'
},
'2010s': {
'description': '現代化轉型',
'buildings': ['現代建築', '超商', '網路設施', '觀光景點'],
'activities': ['服務業', '觀光', '電子商務'],
'population': 3200,
'main_industry': '服務業+觀光'
},
'2024': {
'description': 'AI科技時代',
'buildings': ['智慧建築', 'AI市集', '數位中心', '科技展館'],
'activities': ['AI應用', '創新創業', '數位服務'],
'population': 3800,
'main_industry': '科技創新+觀光'
}
}
def get_period_data(self, period):
return self.historical_periods.get(period, {})
def generate_vr_scene_config(self, period):
"""為VR場景生成配置檔案"""
period_data = self.get_period_data(period)
scene_config = {
'period': period,
'skybox': f'skybox_{period}',
'terrain': f'terrain_{period}',
'buildings': [
{
'name': building,
'model': f'models/{building.replace(" ", "_")}_{period}',
'position': self.get_building_position(building, period),
'interactive': True
}
for building in period_data.get('buildings', [])
],
'characters': [
{
'type': 'farmer' if period in ['1970s', '1990s'] else 'citizen',
'animation': self.get_character_animation(period),
'dialogue': self.get_character_dialogue(period)
}
],
'ambient_sounds': [
f'audio/ambient_{period}_1.mp3',
f'audio/ambient_{period}_2.mp3'
],
'narration': f'audio/narration_{period}.mp3'
}
return scene_config
import asyncio
import websocket
from google.cloud import firestore, monitoring_v3
import json
from datetime import datetime
class RealTimeMonitoringSystem:
def __init__(self):
self.db = firestore.Client()
self.monitoring_client = monitoring_v3.MetricServiceClient()
self.websocket_clients = []
async def start_monitoring(self):
"""啟動即時監控系統"""
# 同時啟動多個監控任務
await asyncio.gather(
self.monitor_visitor_count(),
self.monitor_system_performance(),
self.monitor_social_media(),
self.monitor_exhibitor_status(),
self.websocket_server()
)
async def monitor_visitor_count(self):
"""監控參觀人數"""
while True:
try:
# 從各個數據源收集人數資料
physical_count = await self.get_physical_visitor_count()
online_count = await self.get_online_viewer_count()
visitor_data = {
'timestamp': datetime.now(),
'physical_visitors': physical_count,
'online_viewers': online_count,
'total_engagement': physical_count + online_count
}
# 儲存到資料庫
self.db.collection('monitoring').document('visitors').set(visitor_data)
# 推送到WebSocket客戶端
await self.broadcast_data('visitor_count', visitor_data)
# 檢查是否需要警告
if physical_count > 9000: # 超過安全容量
await self.send_alert('visitor_overflow', physical_count)
await asyncio.sleep(30) # 每30秒更新一次
except Exception as e:
print(f"監控訪客數量時發生錯誤:{e}")
await asyncio.sleep(60)
async def get_physical_visitor_count(self):
"""獲取實體參觀人數"""
# 模擬從入口感應器獲取數據
# 實際應用中這裡會連接到IoT設備
sensors_data = await self.query_iot_sensors()
return sum(sensor['count'] for sensor in sensors_data)
async def get_online_viewer_count(self):
"""獲取線上觀看人數"""
# 從YouTube API獲取直播觀看數
youtube_viewers = await self.get_youtube_live_viewers()
# 從Facebook API獲取直播觀看數
facebook_viewers = await self.get_facebook_live_viewers()
# 從網站Analytics獲取即時用戶數
website_users = await self.get_website_realtime_users()
return youtube_viewers + facebook_viewers + website_users
async def monitor_system_performance(self):
"""監控系統效能"""
while True:
try:
performance_data = {
'timestamp': datetime.now(),
'cpu_usage': await self.get_cpu_usage(),
'memory_usage': await self.get_memory_usage(),
'network_bandwidth': await self.get_network_usage(),
'api_response_time': await self.get_api_response_time(),
'database_connections': await self.get_db_connections()
}
# 儲存效能數據
self.db.collection('monitoring').document('performance').set(performance_data)
# 檢查效能警告閾值
await self.check_performance_alerts(performance_data)
# 推送到監控面板
await self.broadcast_data('system_performance', performance_data)
await asyncio.sleep(60) # 每分鐘更新一次
except Exception as e:
print(f"監控系統效能時發生錯誤:{e}")
await asyncio.sleep(60)
async def monitor_social_media(self):
"""監控社群媒體反應"""
while True:
try:
social_data = {
'timestamp': datetime.now(),
'hashtag_mentions': await self.count_hashtag_mentions('#南桃AI市集'),
'facebook_engagement': await self.get_facebook_engagement(),
'instagram_posts': await self.get_instagram_posts(),
'twitter_mentions': await self.get_twitter_mentions(),
'sentiment_analysis': await self.analyze_social_sentiment()
}
# 儲存社群數據
self.db.collection('monitoring').document('social_media').set(social_data)
# 推送到儀表板
await self.broadcast_data('social_media', social_data)
await asyncio.sleep(300) # 每5分鐘更新一次
except Exception as e:
print(f"監控社群媒體時發生錯誤:{e}")
await asyncio.sleep(300)
async def websocket_server(self):
"""WebSocket伺服器用於即時推送數據"""
async def handle_client(websocket, path):
self.websocket_clients.append(websocket)
try:
await websocket.wait_closed()
finally:
self.websocket_clients.remove(websocket)
start_server = websockets.serve(handle_client, "localhost", 8765)
await start_server
async def broadcast_data(self, data_type, data):
"""向所有WebSocket客戶端廣播數據"""
message = {
'type': data_type,
'data': data,
'timestamp': datetime.now().isoformat()
}
if self.websocket_clients:
await asyncio.gather(
*[client.send(json.dumps(message)) for client in self.websocket_clients],
return_exceptions=True
)
# 監控面板前端
monitoring_dashboard_html = """
<!DOCTYPE html>
<html>
<head>
<title>南桃AI市集即時監控</title>
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
<style>
body { font-family: Arial, sans-serif; margin: 0; padding: 20px; }
.dashboard { display: grid; grid-template-columns: 1fr 1fr; gap: 20px; }
.widget { background: white; border: 1px solid #ddd; border-radius: 8px; padding: 20px; }
.metric { font-size: 24px; font-weight: bold; color: #333; }
.label { font-size: 14px; color: #666; margin-bottom: 10px; }
.alert { background: #ff4444; color: white; padding: 10px; border-radius: 4px; margin: 10px 0; }
.success { background: #44ff44; color: white; padding: 10px; border-radius: 4px; margin: 10px 0; }
</style>
</head>
<body>
<h1>🏟️ 南桃AI市集即時監控台</h1>
<div class="dashboard">
<div class="widget">
<div class="label">現場參觀人數</div>
<div class="metric" id="physical-visitors">-</div>
</div>
<div class="widget">
<div class="label">線上觀看人數</div>
<div class="metric" id="online-viewers">-</div>
</div>
<div class="widget">
<div class="label">系統CPU使用率</div>
<div class="metric" id="cpu-usage">-</div>
</div>
<div class="widget">
<div class="label">網路頻寬使用</div>
<div class="metric" id="network-usage">-</div>
</div>
<div class="widget">
<div class="label">社群媒體提及數</div>
<div class="metric" id="social-mentions">-</div>
</div>
<div class="widget">
<div class="label">情感分析</div>
<div class="metric" id="sentiment-score">-</div>
</div>
</div>
<div id="alerts"></div>
<script>
const ws = new WebSocket('ws://localhost:8765');
ws.onmessage = function(event) {
const message = JSON.parse(event.data);
updateDashboard(message.type, message.data);
};
function updateDashboard(type, data) {
switch(type) {
case 'visitor_count':
document.getElementById('physical-visitors').textContent =
data.physical_visitors.toLocaleString();
document.getElementById('online-viewers').textContent =
data.online_viewers.toLocaleString();
break;
case 'system_performance':
document.getElementById('cpu-usage').textContent =
data.cpu_usage + '%';
document.getElementById('network-usage').textContent =
data.network_bandwidth + ' Mbps';
break;
case 'social_media':
document.getElementById('social-mentions').textContent =
data.hashtag_mentions.toLocaleString();
document.getElementById('sentiment-score').textContent =
data.sentiment_analysis.score;
break;
}
}
function showAlert(message, type = 'alert') {
const alertDiv = document.createElement('div');
alertDiv.className = type;
alertDiv.textContent = message;
document.getElementById('alerts').appendChild(alertDiv);
setTimeout(() => {
alertDiv.remove();
}, 5000);
}
</script>
</body>
</html>
"""
# docker-compose.yml
version: '3.8'
services:
# 前端Web應用
frontend:
build: ./frontend
ports:
- "3000:3000"
environment:
- REACT_APP_API_URL=http://api:8000
depends_on:
- api
# 後端API服務
api:
build: ./backend
ports:
- "8000:8000"
environment:
- DATABASE_URL=postgresql://user:pass@db:5432/nantao_market
- REDIS_URL=redis://redis:6379
- GEMINI_API_KEY=${GEMINI_API_KEY}
depends_on:
- db
- redis
volumes:
- ./logs:/app/logs
# 資料庫
db:
image: postgres:15
environment:
- POSTGRES_DB=nantao_market
- POSTGRES_USER=user
- POSTGRES_PASSWORD=pass
volumes:
- postgres_data:/var/lib/postgresql/data
- ./init.sql:/docker-entrypoint-initdb.d/init.sql
# Redis快取
redis:
image: redis:7-alpine
ports:
- "6379:6379"
# 監控系統
monitoring:
build: ./monitoring
ports:
- "8765:8765"
environment:
- GOOGLE_CLOUD_PROJECT=${GOOGLE_CLOUD_PROJECT}
volumes:
- ./monitoring/config:/app/config
# Nginx反向代理
nginx:
image: nginx:alpine
ports:
- "80:80"
- "443:443"
volumes:
- ./nginx.conf:/etc/nginx/nginx.conf
- ./ssl:/etc/nginx/ssl
depends_on: